#pragma once

#ifdef ESP32
#if __has_include("soc/soc_caps.h")
# include "soc/soc_caps.h"
#else
# error "No ESP capability header found"
#endif

#if (SOC_LCD_I80_SUPPORTED && SOC_LCDCAM_I80_NUM_BUSES && !SOC_PARLIO_GROUPS)

#include "uDisplay_panel.h"

#include "esp_private/gdma.h"
#include <hal/gpio_ll.h>
#include <hal/lcd_hal.h>
#include <soc/lcd_cam_reg.h>
#include <soc/lcd_cam_struct.h>
#include "esp_rom_lldesc.h"
#include "esp_lcd_io_i80.h"
#include "esp_private/gdma.h"
#include <hal/gpio_ll.h>
#include <hal/lcd_hal.h>
#include <soc/lcd_cam_reg.h>
#include <soc/lcd_cam_struct.h>
#include "esp_pm.h"
#include <hal/dma_types.h>
#include <rom/cache.h>
#include "esp_rom_lldesc.h"
#include <rom/gpio.h>

/**
 * Configuration for I80 (8080/6800) parallel displays
 */
struct I80PanelConfig {
    // ===== Display Dimensions =====
    uint16_t width;
    uint16_t height;
    uint8_t bpp;              // bits per pixel (typically 16)
    uint8_t color_mode;       // color mode (16, 18, etc.)

    // ===== Bus Configuration =====
    int8_t cs_pin;            // Chip select
    int8_t dc_pin;            // Data/Command
    int8_t wr_pin;            // Write strobe
    int8_t rd_pin;            // Read strobe (-1 if not used)
    int8_t data_pins_low[8];  // D0-D7 pins
    int8_t data_pins_high[8]; // D8-D15 pins (for 16-bit bus)
    uint8_t bus_width;        // 8 or 16
    uint32_t clock_speed_hz;  // Bus clock speed

    // ===== Display Commands =====
    uint8_t cmd_set_addr_x;   // Column address command
    uint8_t cmd_set_addr_y;   // Row/page address command
    uint8_t cmd_write_ram;    // Write to RAM command
    uint8_t cmd_madctl;       // Memory access control command (typically 0x36)
    uint8_t cmd_startline;    // Start line command (for sa_mode == 8)

    // ===== Display Modes =====
    uint8_t sa_mode;          // Set address mode (8 = special, 16 = normal)
    uint8_t allcmd_mode;      // If true, send data as commands

    // ===== Per-Rotation Configuration =====
    uint8_t rot_cmd[4];        // MADCTL rotation command value per rotation
    uint16_t x_addr_offset[4]; // Address offset per rotation
    uint16_t y_addr_offset[4];

    // ===== Initialization =====
    uint8_t* init_commands;
    uint16_t init_commands_count;
};

class I80Panel : public UniversalPanel {
public:
    /**
     * Constructor - receives configuration struct
     */
    I80Panel(const I80PanelConfig& config);
    virtual ~I80Panel();

    // UniversalPanel interface
    bool drawPixel(int16_t x, int16_t y, uint16_t color) override;
    bool fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) override;
    bool pushColors(uint16_t *data, uint16_t len, bool first = false) override;
    bool setAddrWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1) override;
    bool drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) override;
    bool drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) override;
    bool displayOnff(int8_t on) override;
    bool invertDisplay(bool invert) override;
    bool setRotation(uint8_t rotation) override;
    bool updateFrame() override;
    
    // Simple resistive touch hack
    uint32_t getSimpleResistiveTouch(uint32_t threshold);

    // DMA functionality
    bool initDMA();
    void deInitDMA();
    bool dmaBusy();
    void dmaWait();

private:
    // ===== Hardware & Configuration =====
    I80PanelConfig cfg;        // Copy of config
    
    // ===== Display State =====
    int16_t _width, _height;
    uint8_t _rotation;
    
    // I80 hardware handles
    esp_lcd_i80_bus_handle_t _i80_bus;
    volatile lcd_cam_dev_t* _dev;
    uint32_t _clock_reg_value;

    // DMA resources
    bool _DMA_Enabled;
    gdma_channel_handle_t _dma_chan;
    lldesc_t *_dmadesc;
    uint32_t _dmadesc_size;

    // Current address window
    int16_t _addr_x0, _addr_y0, _addr_x1, _addr_y1;

    // Low-level I80 functions
    void calcClockDiv(uint32_t* div_a, uint32_t* div_b, uint32_t* div_n, uint32_t* clkcnt, uint32_t baseClock, uint32_t targetFreq);
    void _alloc_dmadesc(size_t len);
    void _setup_dma_desc_links(const uint8_t *data, int32_t len);
    void pb_beginTransaction(void);
    void pb_endTransaction(void);
    void pb_wait(void);
    bool pb_busy(void);
    void _pb_init_pin(bool read);
    bool pb_writeCommand(uint32_t data, uint_fast8_t bit_length);
    void pb_writeData(uint32_t data, uint_fast8_t bit_length);
    void pb_writeBytes(const uint8_t* data, uint32_t length, bool use_dma);
    void pb_pushPixels(uint16_t* data, uint32_t length, bool swap_bytes, bool use_dma);
    void cs_control(bool level);
    
    // Color mode helpers
    void writeColor(uint16_t color);
    void setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
};
#endif // SOC_LCD_I80_SUPPORTED && SOC_LCDCAM_I80_NUM_BUSES
#endif // ESP32